package jp.ac.waseda.cs.washi.game.api;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;

import jp.ac.waseda.cs.washi.gameaiarena.api.Point2;

/**
 * ゲーム内に登場するボーナス図形（ミニパンプキンおよびキングパンプキン）の種類を表します。
 */
public enum PumpkinType {

	/**
	 * ミニパンプキン
	 */
	MiniPumpkin(new int[][] { { 0, 0, 2, 0, 0 }, { 0, 2, 1, 2, 0 },
			{ 2, 1, 1, 1, 2 }, { 0, 2, 1, 2, 0 }, { 0, 0, 2, 0, 0 }, }, 20,
			new Point2(2, 2), new Point2[] { new Point2(2, 2), }),

	/**
	 * キングパンプキン
	 */
	KingPumpkin(new int[][] { { 0, 0, 0, 2, 0, 0, 0 }, { 0, 0, 2, 1, 2, 0, 0 },
			{ 0, 2, 1, 1, 1, 2, 0 }, { 2, 1, 1, 1, 1, 1, 2 },
			{ 0, 2, 1, 1, 1, 2, 0 }, { 0, 0, 2, 1, 2, 0, 0 },
			{ 0, 0, 0, 2, 0, 0, 0 }, }, 80, new Point2(3, 3),
			new Point2[] { new Point2(2, 2), new Point2(2, 4), new Point2(4, 2),
					new Point2(4, 4), });

	private final PumpkinElement[][] _elements;

	private final int _score;

	private final Point2 _firstMineLocation;

	private final Point2 _centerLocation;

	private final List<Point2> _anyLocations;

	private final List<Point2> _ownedLocations;

	private final List<Point2> _notOwnedLocations;

	private final List<Point2> _coreLocations;

	private PumpkinType(int[][] shape, int score, Point2 centerPoint,
			Point2[] cores) {
		PumpkinElement[] shapeElements = PumpkinElement.values();
		int w = shape[0].length, h = shape.length;
		_elements = new PumpkinElement[h][w];
		for (int y = 0; y < h; y++) {
			for (int x = 0; x < w; x++) {
				_elements[y][x] = shapeElements[shape[y][x]];
			}
		}
		_score = score;
		_firstMineLocation = initializeBaseLocation();
		_centerLocation = centerPoint;

		ArrayList<Point2> anyLocations = new ArrayList<Point2>();
		ArrayList<Point2> mineLocations = new ArrayList<Point2>();
		ArrayList<Point2> notMineLocations = new ArrayList<Point2>();
		for (Point2 p : Point2.getPoints(getWidth(), getHeight())) {
			switch (getElement(p)) {
			case ANY:
				anyLocations.add(p);
				break;
			case OWNED:
				mineLocations.add(p);
				break;
			case NOT_OWNED:
				notMineLocations.add(p);
				break;
			}
		}

		_anyLocations = Collections.unmodifiableList(anyLocations);
		_ownedLocations = Collections.unmodifiableList(mineLocations);
		_notOwnedLocations = Collections.unmodifiableList(notMineLocations);
		_coreLocations = Collections.unmodifiableList(Arrays.asList(cores));
	}

	private Point2 initializeBaseLocation() {
		for (int y = 0; y < getHeight(); y++) {
			for (int x = 0; x < getWidth(); x++) {
				if (getElement(x, y) == PumpkinElement.OWNED)
					return new Point2(x, y);
			}
		}
		throw new RuntimeException();
	}

	/**
	 * ボーナス図形を形成する2次元配列内において、ボーナス図形の成立に影響を与えないマスの座標のリストを返します。
	 * (例えばミニパンプキンの場合は、角の四隅部分、つまり、(0,0), (1,0), (0,1), (0,4), (0,3), (1,4),
	 * (4,4), (4,3), (3,4), (4,0), (3,0), (4,1))
	 * 
	 * @return ボーナス図形を形成する2次元配列内において、ボーナス図形の成立に影響を与えないマスの座標のリスト
	 */
	public List<Point2> getAnyLocations() {
		return _anyLocations;
	}

	/**
	 * ボーナス図形を形成する2次元配列内において、ボーナス図形の成立に必要なマスの座標のリストを返します。
	 * (例えばミニパンプキンの場合は、十字部分、つまり、(2,1), (2,2), (1,2), (3,2), (2,3))
	 * 
	 * @return ボーナス図形が所有する2次元配列内において、ボーナス図形の成立に必要なマスの座標のリスト
	 */
	public List<Point2> getOwnedLocations() {
		return _ownedLocations;
	}

	/**
	 * ボーナス図形を形成する2次元配列内において、開発するとボーナス図形が成立しなくなるマスの座標のリストを返します。
	 * (例えばミニパンプキンの場合は、十字部分の周辺、つまり、(2,0), (3,1), (4,2), (3,3), (2,4), (1,3),
	 * (0,2), (1,1))
	 * 
	 * @return　ボーナス図形が所有する2次元配列内において、開発するとボーナス図形が成立しなくなるマスの座標のリスト
	 */
	public List<Point2> getNotOwnedLocations() {
		return _notOwnedLocations;
	}

	/**
	 * 指定した位置に対応するボーナス図形を形成するPumpkinElementを返します。
	 * 
	 * @param p
	 *            ボーナス図形を形成する2次元配列内の座標
	 * @return 指定した位置に対応するボーナス図形を形成するPumpkinElement
	 */
	public PumpkinElement getElement(Point2 p) {
		return getElement(p.x, p.y);
	}

	/**
	 * 指定した位置に対応するボーナス図形を形成するPumpkinElementを返します。
	 * 
	 * @param x
	 *            ボーナス図形を形成する2次元配列内のx座標
	 * @param y
	 *            ボーナス図形を形成する2次元配列内のy座標
	 * @return 指定した位置に対応するボーナス図形を形成するPumpkinElement
	 */
	public PumpkinElement getElement(int x, int y) {
		return _elements[y][x];
	}

	/**
	 * ボーナス図形を形成する2次元配列の横の長さを返します。
	 * 
	 * @return ボーナス図形を形成する2次元配列の横の長さ
	 */
	public int getWidth() {
		return _elements[0].length;
	}

	/**
	 * ボーナス図形を形成する2次元配列の縦の長さを返します。
	 * 
	 * @return ボーナス図形を形成する2次元配列の縦の長さ
	 */
	public int getHeight() {
		return _elements.length;
	}

	public Point2 getFirstMineLocation() {
		return _firstMineLocation;
	}

	/**
	 * ボーナス図形を形成する2次元配列の中心の座標を返します。 (例えばミニパンプキンの場合は、余白を含む5×5の正方形の中心であるPoint(2,
	 * 2)が返り値となります。)
	 * 
	 * @return　ボーナス図形を形成する2次元配列の中心の座標
	 */
	public Point2 getCenterLocation() {
		return _centerLocation;
	}

	/**
	 * ボーナス図形が保有するパンプキンマス（強度を持つマス）のリストを返します。
	 * 
	 * @return ボーナス図形が保有するパンプキンマス（強度を持つマス）のリスト
	 */
	public List<Point2> getCoreLocations() {
		return _coreLocations;
	}

	/**
	 * ボーナス図形の成立によって加算されるスコアを返します。
	 * 
	 * @return ボーナス図形の成立によって加算されるスコア
	 */
	public int getScore() {
		return _score;
	}
}
